#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <unordered_map>
#include <mutex>

#include "util.hpp"
#include "log.hpp"

namespace ns_index
{
    using namespace ns_log;
    struct DocInfo
    {
        std::string title;
        std::string content;
        std::string url;
        uint64_t doc_id;
    };

    struct InvertedElem
    {
        uint64_t doc_id;
        std::string word;
        int weight; // 相关性
    };

    // 倒排拉链
    typedef std::vector<InvertedElem> InvertedList;

    class Index
    {
    private:
        // 正排索引的数据结构⽤数组，数组的下标天然是⽂档的ID
        std::vector<DocInfo> forward_index; // 正排索引
        std::unordered_map<std::string, InvertedList> inverted_index;// 倒排索引⼀定是⼀个关键字和⼀组(个)InvertedElem对应[关键字和倒排拉链的映射关系]
        

        // index的空间巨大，我们使用单例的形式来使用
    private:
        Index() {}//构造函数必须要有结构体，就算没有内容也得有结构体，不然没法创建
        Index(const Index& index)=default;
        Index& operator=(const Index& index)=default;
        static Index* instance;
        static std::mutex mtx;
    public:
        ~Index() {}

    public:
        static Index* Getinstance()
        {
            if(instance==nullptr)
            {
                mtx.lock();
                if(instance==nullptr)//保障线程安全，拿到锁的时候的状态
                {
                    instance=new Index;
                }
                mtx.unlock();
            }
            return instance;
        }

        // 由于我们类内存会比较大，我们接口返回值用指针的形式返回
        // 根据doc_id找到找到⽂档内容
        DocInfo *Getforward(uint64_t doc_id)
        {
            if (doc_id > forward_index.size())
            {
                std::cerr << "doc_id :" << doc_id << " error" << std::endl;
                return nullptr;
            }
            return &forward_index[doc_id];
        }
        // 根据关键字string，获得倒排拉链
        InvertedList *GetInverted(const std::string &word)
        {
            //这里有问题
            auto iter = inverted_index.find(word);
            if (iter == inverted_index.end())
            {
                std::cerr << "not find " << word << std::endl;
                return nullptr;
            }
            return &(iter->second);
        }
        // 根据去标签，格式化之后的⽂档，构建正排和倒排索引
        // data/raw_html/raw.txt
        bool BuildIndex(const std::string &input) // parse处理完毕的数据交给我
        {
            //读文件
            std::ifstream in(input, std::ios::in | std::ios::binary);
            if (!in.is_open())
            {
                std::cerr << "open input error ! inputL" << input << std::endl;
                return false;
            }

            std::string line;
            int count=0;
            while (getline(in, line))
            {
                DocInfo *doc = BuildForwardIndex(line);
                if (nullptr == doc)
                {
                    std::cerr << "build " << line << " error" << std::endl; // for debug
                    continue;
                }
                BuildIvertedIndex(*doc);
                count++;
                if(count%50==0)
                {
                    LOG(INFO)<<"正排倒排已经完成文件数："<<count<<std::endl;
                }
            }
            return true;
        }

    private:
        DocInfo *BuildForwardIndex(const std::string &line)
        {
            // 1.切割字符串 title\3content\3url\n
            std::vector<std::string> results;
            std::string sep = "\3";
            ns_util::StringUtil::CutString(line, &results, sep);
            if (results.size() != 3)
            {
                return nullptr;
            }

            // 2.把切割好的信息放入doc
            DocInfo doc;
            doc.title = results[0];
            doc.content = results[1];
            doc.url = results[2];
            doc.doc_id = results.size();

            // 3.插入到正排索引里
            forward_index.push_back(doc);
            return &forward_index.back();
        }
        bool BuildIvertedIndex(const DocInfo &doc)
        {
            // doc(title content url doc_id)
            // 倒排拉链
            struct word_cnt
            {
                int title_cnt;
                int content_cnt;

                word_cnt() : title_cnt(0), content_cnt(0) {}
            };

            std::unordered_map<std::string, word_cnt> map;

            // 标题分词
            std::vector<std::string> title_word;
            ns_util::JiebaUtil::CutString(doc.title, &title_word);
            for (auto &s : title_word)
            {
                // 忽略大小写
                boost::to_lower(s);
                map[s].title_cnt++; // 有就++，没有就创建
            }

            // 内容分词
            std::vector<std::string> content_word;
            ns_util::JiebaUtil::CutString(doc.content, &content_word);
            for (auto &s : content_word)
            {
                // 忽略大小写
                boost::to_lower(s);
                map[s].content_cnt++; // 有就++，没有就创建
            }

#define X 10
#define Y 1
            for (auto &word_pair : map)
            {
                InvertedElem item;
                item.doc_id = doc.doc_id;
                item.word = word_pair.first;
                item.weight = X * word_pair.second.title_cnt + Y * word_pair.second.content_cnt; // 相关性

                InvertedList &inverted_list = inverted_index[word_pair.first];
                inverted_list.push_back(item);
            }
            return true;
        }
    };
   Index* Index::instance=nullptr;
   std::mutex Index::mtx;
};